`include "defines.v"
module mem(
	input wire rst,
	input wire[31:0] write_data_i,
	input wire write_ce_i,
	input wire[4:0] write_addr_i,
	output reg[31:0] write_data_o,
	output reg write_ce_o,
	output reg[4:0] write_addr_o,
	
	//传给id模块的将要写的数据
	output wire[4:0] mem_id_write_addr_o,
	output wire mem_id_write_ce_o,
	output wire[31:0] mem_id_write_data_o,
	
	//与hilo寄存器相关的数据
	input wire[31:0] hi_i,
	input wire[31:0] lo_i,
	input wire hilo_write_ce_i,
	output reg[31:0] hi_o,
	output reg[31:0] lo_o,
	output reg hilo_write_ce_o,	
	//访存指令信号
	input wire[`AluOpWidth-1:0] alu_op_i,
	input wire[31:0] mem_addr_i,
	input wire[31:0] reg2_i,
	input wire[31:0] mem_rdata_i, //从数据存储器读到的数据
	output reg mem_ce_o, //数据存储器使能信号
	output wire mem_write_ce_o, //写使能信号
	output reg[31:0] mem_wdata_o, 
	output reg[31:0] mem_addr_o,
	output reg[3:0] byte_sel_o, //字节选择信号
	//ll,sc指令相关信号
	input wire LLbit_i,
	input wire wb_LLbit_wCe_i,
	input wire wb_LLbit_wData_i,
	output reg LLbit_wData_o,
	output reg LLbit_wCe_o,
	
	//CP0相关信号
	input wire[31:0] cp0_wData_i,
	input wire[4:0] cp0_wAddr_i,
	input wire cp0_wCe_i,
	output wire[31:0] cp0_wData_o,
	output wire[4:0] cp0_wAddr_o,
	output wire cp0_wCe_o,
	//异常相关
	input wire[31:0] exception_type_i, //异常类型
	input wire[31:0] current_inst_addr_i, //当前访存阶段指令的地址
	input wire is_in_delaySlot_i, //访存阶段是否是延迟槽指令
		//CP0的输入
	input wire[31:0] cp0_status_i,
	input wire[31:0] cp0_cause_i,
	input wire[31:0] cp0_epc_i,
		//wb对CP0的信号
	input wire[31:0] wb_cp0_wData_i,
	input wire[4:0] wb_cp0_wAddr_i,
	input wire wb_cp0_wCe_i,
		
	output reg[31:0] exception_type_o, //异常类型
	output wire[31:0] current_inst_addr_o, //当前访存阶段指令的地址
	output wire is_in_delaySlot_o, //访存阶段是否是延迟槽指令
	output wire[31:0] cp0_epc_o //CP0中EPC寄存器的最新值
	
);
	
	assign cp0_wData_o = cp0_wData_i;
	assign cp0_wAddr_o = cp0_wAddr_i;
	assign cp0_wCe_o = cp0_wCe_i;
	//计算最新的LLbit值
	wire LLbit = (wb_LLbit_wCe_i == `WriteEnable)? wb_LLbit_wData_i : LLbit_i; 
	
	//异常相关
	assign is_in_delaySlot_o = is_in_delaySlot_i;
	assign current_inst_addr_o = current_inst_addr_i;
	
	reg[31:0] CP0_STATUS; //status最新值
	reg[31:0] CP0_CAUSE; //cause最新值
	reg[31:0] CP0_EPC; //epc最新值
	
	//更新status为最新值
	always@(*)
		if(rst == `RstEnable)		
			CP0_STATUS = `ZeroWord;
		else if(wb_cp0_wCe_i == `WriteEnable && wb_cp0_wAddr_i == `CP0_REG_STATUS)
			CP0_STATUS = wb_cp0_wData_i;
		else 
			CP0_STATUS = cp0_status_i;
	//更新cause为最新值
	always@(*)
		if(rst == `RstEnable)		
			CP0_CAUSE = `ZeroWord;
		else if(wb_cp0_wCe_i == `WriteEnable && wb_cp0_wAddr_i == `CP0_REG_CAUSE)
			CP0_CAUSE = wb_cp0_wData_i;
		else 
			CP0_CAUSE = cp0_cause_i;
	
	//更新epc为最新值
	always@(*)
		if(rst == `RstEnable)
			CP0_EPC = `ZeroWord;
		else if(wb_cp0_wCe_i == `WriteEnable && wb_cp0_wAddr_i == `CP0_REG_EPC)
			CP0_EPC = wb_cp0_wData_i;
		else
			CP0_EPC = cp0_epc_i;
			
	assign cp0_epc_o = CP0_EPC;
	
	//更新最终的异常类型
	always@(*)
		if(rst == `RstEnable)
			exception_type_o = `ZeroWord;
		else if(current_inst_addr_i != `ZeroWord)
		begin
			//中断使能，未发生异常，未屏蔽中断，有中断发生
			if(CP0_STATUS[0] == 1'b1 && CP0_STATUS[1] == 1'b0 &&
			((CP0_CAUSE[15:8] & CP0_STATUS[15:8]) != 8'h00))
				exception_type_o = 32'h0000_0001; //发生中断
			else if(exception_type_i[8] == 1'b1)
				exception_type_o = 32'h0000_0008; //系统调用
			else if(exception_type_i[9] == 1'b1)
				exception_type_o = 32'h0000_000a; //无效指令
			else if(exception_type_i[10] == 1'b1)
				exception_type_o = 32'h0000_000d; //自陷指令
			else if(exception_type_i[11] == 1'b1)
				exception_type_o = 32'h0000_000c; //溢出
			else if(exception_type_i[12] == 1'b1) 
				exception_type_o = 32'h0000_000e; //异常返回
			else
				exception_type_o = `ZeroWord;
		end
		else
			exception_type_o = `ZeroWord;
	
	//如果发生异常，取消对ram的写操作
	reg ram_wCe;
	assign mem_write_ce_o = ram_wCe & (~(|exception_type_o));
	
	always@(*)
		if(rst == `RstEnable)
		begin
			write_data_o = `ZeroWord;
			write_ce_o = `WriteDisable;
 			write_addr_o = 5'b00000;
			hi_o = `ZeroWord;
			lo_o = `ZeroWord;
			hilo_write_ce_o = `WriteDisable;
			
			mem_ce_o = `RamDisable;
			ram_wCe = `WriteDisable;
			mem_wdata_o = `ZeroWord;
			mem_addr_o = `ZeroWord;
			byte_sel_o = 4'b0000;
				
			LLbit_wData_o = 1'b0;
			LLbit_wCe_o = `WriteDisable;
		end
		else
		begin
			write_data_o = write_data_i;
			write_ce_o = write_ce_i; 
			write_addr_o = write_addr_i;
			hi_o = hi_i;
			lo_o = lo_i;
			hilo_write_ce_o <= hilo_write_ce_i;
			
			mem_ce_o = `RamDisable;
			ram_wCe = `WriteDisable;
			mem_wdata_o = `ZeroWord;
			mem_addr_o = `ZeroWord;
			byte_sel_o = 4'b0000;
			
			LLbit_wData_o = 1'b0;
			LLbit_wCe_o = `WriteDisable;
			case(alu_op_i)
				`ALU_LB_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					case(mem_addr_i[1:0])
						2'b00:
						begin
							write_data_o = {{24{mem_rdata_i[31]}}, mem_rdata_i[31:24]};
							// byte_sel_o = 4'b1000;
						end
						2'b01:
						begin
							write_data_o = {{24{mem_rdata_i[23]}}, mem_rdata_i[23:16]};
							// byte_sel_o = 4'b0100;
						end
						2'b10:
						begin
							write_data_o = {{24{mem_rdata_i[15]}}, mem_rdata_i[15:8]};
							// byte_sel_o = 4'b0010;
						end
						2'b11:
						begin
							write_data_o = {{24{mem_rdata_i[7]}}, mem_rdata_i[7:0]};
							// byte_sel_o = 4'b0001;
						end
					endcase
				end
				`ALU_LBU_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					case(mem_addr_i[1:0])
						2'b00:
						begin
							write_data_o = {24'h000000, mem_rdata_i[31:24]};
							// byte_sel_o = 4'b1000;
						end
						2'b01:
						begin
							write_data_o = {24'h000000, mem_rdata_i[23:16]};
							// byte_sel_o = 4'b0100;
						end
						2'b10:
						begin
							write_data_o = {24'h000000, mem_rdata_i[15:8]};
							// byte_sel_o = 4'b0010;
						end
						2'b11:
						begin
							write_data_o = {24'h000000, mem_rdata_i[7:0]};
							// byte_sel_o = 4'b0001;
						end
					endcase
				end
				
				`ALU_LH_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					case(mem_addr_i[1:0])
						2'b00:
						begin
							write_data_o = {{16{mem_rdata_i[31]}}, mem_rdata_i[31:16]};
							// byte_sel_o = 4'b1100;
						end
						2'b10:
						begin
							write_data_o = {{16{mem_rdata_i[15]}}, mem_rdata_i[15:0]};
							// byte_sel_o = 4'b0011;
						end
					endcase
				end
				`ALU_LHU_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					case(mem_addr_i[1:0])
						2'b00:
						begin
							write_data_o = {16'h0000, mem_rdata_i[31:16]};
							// byte_sel_o = 4'b1100;
						end
						2'b10:
						begin
							write_data_o = {16'h0000, mem_rdata_i[15:0]};
							// byte_sel_o = 4'b0011;
						end
					endcase
				end
				`ALU_LW_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					write_data_o = mem_rdata_i;
					// byte_sel_o = 4'b1111;
				end
				`ALU_LWL_OP:
				begin
					mem_ce_o = `RamEnable;
					// mem_addr_o = {mem_addr_i[31:2], 2'b00};
					mem_addr_o = mem_addr_i;
					// byte_sel_o = 4'b1111;
					case(mem_addr_i[1:0])
						2'b00:
							write_data_o = mem_rdata_i;
						2'b01:
							write_data_o = {mem_rdata_i[23:0], reg2_i[7:0]};
						2'b10:
							write_data_o = {mem_rdata_i[15:0], reg2_i[15:0]};
						2'b11:
							write_data_o = {mem_rdata_i[7:0], reg2_i[23:0]};
					endcase
				end
				`ALU_LWR_OP:
				begin
					mem_ce_o = `RamEnable;
					// mem_addr_o = {mem_addr_i[31:2], 2'b00};
					// byte_sel_o = 4'b1111;
					mem_addr_o = mem_addr_i;
					case(mem_addr_i[1:0])
						2'b00:
							write_data_o = {reg2_i[31:8], mem_rdata_i[31:24]};
						2'b01:
							write_data_o = {reg2_i[31:16], mem_rdata_i[31:16]};
						2'b10:
							write_data_o = {reg2_i[31:24], mem_rdata_i[31:8]};
						2'b11:
							write_data_o = mem_rdata_i;
					endcase
				end
				
				`ALU_SB_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					mem_wdata_o = {reg2_i[7:0],reg2_i[7:0],reg2_i[7:0],reg2_i[7:0]};
					ram_wCe = `WriteEnable;
					case(mem_addr_i[1:0])
						2'b00:
							byte_sel_o = 4'b1000;
						2'b01:
							byte_sel_o = 4'b0100;
						2'b10:
							byte_sel_o = 4'b0010;
						2'b11:
							byte_sel_o = 4'b0001;
					endcase
				end
				`ALU_SH_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					mem_wdata_o = {reg2_i[15:0], reg2_i[15:0]};
					ram_wCe = `WriteEnable;
					case(mem_addr_i[1:0])
						2'b00:
							byte_sel_o = 4'b1100;
						2'b10:
							byte_sel_o = 4'b0011;
					endcase
				end
				`ALU_SW_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					mem_wdata_o = reg2_i;
					ram_wCe = `WriteEnable;
					byte_sel_o = 4'b1111;
				end
				`ALU_SWL_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					ram_wCe = `WriteEnable;
					case(mem_addr_i[1:0])
						2'b00:
						begin
							mem_wdata_o = reg2_i;
							byte_sel_o = 4'b1111;
						end
						2'b01:
						begin
							mem_wdata_o = {8'h00, reg2_i[31:8]};
							byte_sel_o = 4'b0111;
						end
						2'b10:
						begin
							mem_wdata_o = {16'h0000, reg2_i[31:16]};
							byte_sel_o = 4'b0011;
						end
						2'b11:
						begin
							mem_wdata_o = {24'h0000, reg2_i[31:24]};
							byte_sel_o = 4'b0001;
						end
					endcase
				end
				`ALU_SWR_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					ram_wCe = `WriteEnable;
					case(mem_addr_i[1:0])
						2'b00:
						begin
							mem_wdata_o = {reg2_i[7:0], 24'h000000};
							byte_sel_o = 4'b1000;
						end
						2'b01:
						begin
							mem_wdata_o = {reg2_i[15:0], 16'h0000};
							byte_sel_o = 4'b1100;
						end
						2'b10:
						begin
							mem_wdata_o = {reg2_i[23:0], 8'h00};
							byte_sel_o = 4'b1110;
						end
						2'b11:
						begin
							mem_wdata_o = reg2_i;
							byte_sel_o = 4'b1111;
						end
					endcase
				end
				`ALU_LL_OP:
				begin
					mem_ce_o = `RamEnable;
					mem_addr_o = mem_addr_i;
					write_data_o = mem_rdata_i;
					// case(mem_addr_i[1:0])
						// 2'b00:
							// write_data_o = {{24{mem_rdata_i[31]}},mem_rdata_i[31:24]};
						// 2'b01:
							// write_data_o = {{24{mem_rdata_i[23]}},mem_rdata_i[23:16]};
						// 2'b10:
							// write_data_o = {{24{mem_rdata_i[15]}},mem_rdata_i[15:8};
						// 2'b11:
							// write_data_o = {{24{mem_rdata_i[7]}},mem_rdata_i[7:0]};
					// endcase
					LLbit_wData_o = 1'b1;
					LLbit_wCe_o = `WriteEnable;
				end
				`ALU_SC_OP:
				begin
					if(LLbit == 1'b1)
					begin
						mem_ce_o = `RamEnable;
						mem_addr_o = mem_addr_i;
						ram_wCe = `WriteEnable;
						mem_wdata_o = reg2_i;
						byte_sel_o = 4'b1111;
						write_data_o = 32'h0000_0001;
						LLbit_wData_o = 1'b0;
						LLbit_wCe_o = `WriteEnable;
					end
					else	
						write_data_o = 32'h0000_0000;
				end
			endcase
		end
		
	assign mem_id_write_addr_o = write_addr_o; 
	assign mem_id_write_ce_o = write_ce_o;
	assign mem_id_write_data_o = write_data_o;
endmodule
